/*++
 
 Copyright (c) Microsoft Corporation. 

 This source code is subject to terms and conditions of the Microsoft
 Public License. A copy of the license can be found in the License.txt
 file at the root of this distribution. By using this source code in 
 any fashion, you are agreeing to be bound by the terms of the Microsoft
 Public License.

 You must not remove this notice, or any other, from this software.

Module Name:

    Queue.cpp

Abstract:

    This module contains the implementation of the Generic serial driver
    queue callback object.

--*/

#include "internal.h"
#include "Device.h"
#include "Queue.h"

#include "queue.tmh"

/////////////////////////////////////////////////////////////////////////
//
// CMyQueue::CMyQueue
//
// Object constructor function
//
// Initialize member variables
//
/////////////////////////////////////////////////////////////////////////
CMyQueue::CMyQueue() :
    m_pParentDevice(NULL)
{
}

/////////////////////////////////////////////////////////////////////////
//
// CMyQueue::~CMyQueue
//
// Object destructor function
//
//
/////////////////////////////////////////////////////////////////////////
CMyQueue::~CMyQueue()
{
    // Release the reference that was incremented in CreateInstance()
    SAFE_RELEASE(m_pParentDevice);   
}

/////////////////////////////////////////////////////////////////////////
//
// CMyQueue::CreateInstance
//
// This function supports the COM factory creation system
//
// Parameters:
//      parentDevice    - A pointer to the CWSSDevice object
//      ppUkwn          - pointer to a pointer to the queue to be returned
//
// Return Values:
//      S_OK: The queue was created successfully
//
/////////////////////////////////////////////////////////////////////////
HRESULT CMyQueue::CreateInstance(__in IWDFDevice*  pWdfDevice, CMyDevice* pMyDevice)
{
    CComObject<CMyQueue>* pMyQueue = NULL;

    if(NULL == pMyDevice)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = CComObject<CMyQueue>::CreateInstance(&pMyQueue);

    if(SUCCEEDED(hr))
    {
        // AddRef the object
        pMyQueue->AddRef();

        // Store the parent device object
        pMyQueue->m_pParentDevice = pMyDevice;

        // Increment the reference for the lifetime of the CMyQueue object.
        pMyQueue->m_pParentDevice->AddRef();

        CComPtr<IUnknown> spIUnknown;
        hr = pMyQueue->QueryInterface(IID_IUnknown, (void**)&spIUnknown);

        if(SUCCEEDED(hr))
        {
            // Create the framework queue
            CComPtr<IWDFIoQueue> spDefaultQueue;
            hr = pWdfDevice->CreateIoQueue( spIUnknown,
                                            TRUE,                        // DefaultQueue
                                            WdfIoQueueDispatchParallel,  // Parallel queue handling 
                                            FALSE,                       // PowerManaged
                                            TRUE,                        // AllowZeroLengthRequests
                                            &spDefaultQueue 
                                            );

            if (FAILED(hr))
            {
                Trace( TRACE_LEVEL_ERROR, "%!FUNC!: Could not create default I/O queue, %!hresult!", hr);
            }
        }
        // Release the pMyQueue pointer when done. Note: UMDF holds a reference to it above.
        pMyQueue->Release();
    }

    return hr;
}

/////////////////////////////////////////////////////////////////////////
//
// CMyQueue::OnCreateFile
//
// This method is called when an application opens a device by calling CreateFile
//
// Parameters:
//      pQueue            - pointer to an IO queue
//      pRequest          - pointer to an IO request
//      pFileObject       - pointer to the IWDFFile interface for the file object
//
/////////////////////////////////////////////////////////////////////////

STDMETHODIMP_ (void)
CMyQueue::OnCreateFile(
    /*[in]*/ IWDFIoQueue*       pQueue,
    /*[in]*/ IWDFIoRequest*     pRequest,
    /*[in]*/ IWDFFile*          pFileObject
    )
{
    HRESULT hr = S_OK;
    UNREFERENCED_PARAMETER(pQueue);

    // Taken from IWDFFile::GetDevice example   
    CComPtr<IWDFDevice> wdfDevice;    

    // Taken from IWDFFile::RetrieveFileName example
    ULONG fileNameCch = 0;
    PWSTR fileName = NULL;
        
    CComPtr<IWDFDevice> pDevice;
    pQueue->GetDevice(&pDevice);

    // Taken from IWDFFile::GetDevice example   
    pFileObject->GetDevice(&wdfDevice);    

    // Taken from IWDFFile::RetrieveFileName example

    // Get the length of the file name to allocate a buffer.
    hr = pFileObject->RetrieveFileName(NULL, &fileNameCch);
    //
    // Allocate the buffer.
    //
    if (SUCCEEDED(hr))
    {
        fileName = new WCHAR[fileNameCch];

        if (fileName == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
    }
    //
    // Get the file name.
    //
    if (SUCCEEDED(hr))
    {
        hr = pFileObject->RetrieveFileName(fileName, &fileNameCch);
    }


    pRequest->Complete(hr);
    return;
}

/////////////////////////////////////////////////////////////////////////
//
// CMyQueue::OnDeviceIoControl
//
// This method is called when an IOCTL is sent to the device
//
// Parameters:
//      pQueue            - pointer to an IO queue
//      pRequest          - pointer to an IO request
//      ControlCode       - The IOCTL to process
//      InputBufferSizeInBytes - the size of the input buffer
//      OutputBufferSizeInBytes - the size of the output buffer
//
/////////////////////////////////////////////////////////////////////////
STDMETHODIMP_ (void) CMyQueue::OnDeviceIoControl(
    __in IWDFIoQueue*     pQueue,
    __in IWDFIoRequest*   pRequest,
    __in ULONG            ControlCode,
         SIZE_T           InputBufferSizeInBytes,
         SIZE_T           OutputBufferSizeInBytes
    )
{
    UNREFERENCED_PARAMETER(pQueue);
    UNREFERENCED_PARAMETER(InputBufferSizeInBytes);
    UNREFERENCED_PARAMETER(OutputBufferSizeInBytes);
    
    DWORD dwWritten = 0;

    if( IS_WPD_IOCTL( ControlCode ) )
    {
        m_pParentDevice->ProcessIoControl(  pQueue,
                                            pRequest,
                                            ControlCode,
                                            InputBufferSizeInBytes,
                                            OutputBufferSizeInBytes,
                                            &dwWritten);
    }
    else
    {
        // Unsupported request
        pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
    }

}
